#include "cFileIO.h"

cFileIO::cFileIO() {
	error_info = NULL;
}

cFileIO::~cFileIO() {
	if(error_info != NULL)
		delete [] error_info;
	map<HANDLE, char*>::iterator itr = handle_buffers.begin();
	while(itr != handle_buffers.end()) {
		UnmapViewOfFile((void*)itr->second);
		itr++;
	}
	CloseAllHeandles();
}

bool cFileIO::CloseHANDLE(HANDLE hndl) {
	return CloseHandle(hndl);
}

void cFileIO::CloseAllHeandles() {
	map<HANDLE, int>::iterator itr = handle_access.begin();
	while(itr != handle_access.end()) {
		CloseHandle(itr->first);
		itr++;
	}
}

const char* cFileIO::GetErrorInformation(__in_opt DWORD *error_code) {
	
	// Retrieve the system error message for the last-error code
	
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError();
	*error_code = dw;
	
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
	
	error << " > Failed with error " << dw << ": " << (LPTSTR)lpMsgBuf << endl;
	
    LocalFree(lpMsgBuf);
	if(error_info != NULL)
		delete [] error_info;
	error_info = new char[error.str().size() + 1];
	memcpy(error_info, error.str().data(), error.str().size());
	error_info[error.str().size()] = '\0';
	
	return error_info;
}

HANDLE cFileIO::OpenFile(__in char *filename, __in int access, __in_opt DWORD *filesize_high, __in_opt DWORD *filesize_low) {
	HANDLE hFile;
	
	if(access == IO_READ)
		hFile = CreateFile(filename, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if(access == IO_READWRITE)
		hFile = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	
	if(hFile == INVALID_HANDLE_VALUE) {
		 error << "[FAIL] CreateFile" << endl;
		 return INVALID_HANDLE_VALUE;
	}
	
	handle_access[hFile] = access;
	
	if(access == IO_READ) {
		DWORD FileSizeHigh = 0;
		DWORD FileSizeLow = GetFileSize(hFile, &FileSizeHigh);
		if(filesize_high)
			*filesize_high = FileSizeHigh;
		if(filesize_low)
			*filesize_low = FileSizeLow;
	}
	
	#if defined(DEBUG) | defined(_DEBUG)
	
	if(hFile != INVALID_HANDLE_VALUE) {
		cout << "[OK] CreateFile" << endl;
		if(access == IO_READ) {
			DWORD FileSizeHigh = 0;
			DWORD FileSizeLow = GetFileSize(hFile, &FileSizeHigh);
			DWORDLONG FileSize = (DWORDLONG) FileSizeHigh * BUFFERSIZEMAX + FileSizeLow;
			cout << " > FileSize: " << FileSize << endl;
		} else {
			cout << " > Opened for write " << endl;
		}
	}
	
	#endif
	
	return hFile;
}

HANDLE cFileIO::CreateMappingOfFile(HANDLE hFile, DWORD filesize_high, DWORD filesize_low) {
	if(hFile == INVALID_HANDLE_VALUE) {
		 error << "[FAIL] Invalid file HANDLE" << endl;
		 return INVALID_HANDLE_VALUE;
	}
	
	HANDLE hMapFile;
	DWORD FileSizeHigh, FileSizeLow;
	DWORD access;
	FileSizeLow = GetFileSize(hFile, &FileSizeHigh);
	if(filesize_high || filesize_low) {
		FileSizeHigh = filesize_high;
		FileSizeLow = filesize_low;
	}
	
	if(handle_access[hFile] == IO_READ)
		access = PAGE_READONLY;
	if(handle_access[hFile] == IO_READWRITE) {
		if(! (filesize_high || filesize_low)) {
			error << "[FAIL]  CreateFileMapping" << endl << " > Wrong size parameters" << endl;
			return NULL;
		}
		access = PAGE_READWRITE;
	}
	
	hMapFile = CreateFileMapping(hFile, NULL, access, FileSizeHigh, FileSizeLow, NULL);
	

	if(hMapFile == NULL) {
		error << "[FAIL] CreateFileMapping" << endl;
		return NULL;
	}
	
	handle_access[hMapFile] = handle_access[hFile];
	
	#if defined(DEBUG) | defined(_DEBUG)
	
	if(hMapFile != NULL) {
		cout << "[OK] CreateFileMapping" << endl;
	}
	
	#endif

	return hMapFile;
}

char* cFileIO::Map(HANDLE hMapFile, DWORD offset_high, DWORD offset_low, SIZE_T size) {
	if(hMapFile == NULL) {
		 error << "[FAIL] Invalid mapping HANDLE" << endl;
		 return NULL;
	}
	if(handle_buffers[hMapFile])
		UnmapViewOfFile((void*)handle_buffers[hMapFile]);

	DWORD access;
	if(handle_access[hMapFile] == IO_READ)
		access = FILE_MAP_READ;
	if(handle_access[hMapFile] == IO_READWRITE)
		access = FILE_MAP_WRITE;
	handle_buffers[hMapFile] = (char*) MapViewOfFile(hMapFile, access, offset_high, offset_low, size);

	if(handle_buffers[hMapFile] == NULL) {
		error << "[FAIL] MapViewOfFile " << endl;
		return NULL;
	}
	
	#if defined(DEBUG) | defined(_DEBUG)
	
	if(hMapFile != NULL) {
		cout << "[OK] MapViewOfFile " << endl;
	}
	
	#endif
	
	return handle_buffers[hMapFile];
}